using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Acms.Bll
{
    public abstract class BaseItem
    {
        protected BaseItem()
        {
            isNewRecord = true;
        }

        protected BaseItem(Hashtable table)
        {
            Load(table);
            concurencyId = LoadConcurencyId(table);
        }

        protected BaseItem(Hashtable table, ICollection collection)
        {
            Load(table);
            concurencyId = LoadConcurencyId(table);
            this.collection = collection;
        }

        protected abstract void Load(Hashtable table);

        private ICollection collection;
        protected ICollection Collection
        {
            get { return collection; }
        }

        private object concurencyId;
        public object ConcurencyId
        {
            get { return concurencyId; }
        }

        protected void SetVersionId(object newVersionId)
        {
            concurencyId = newVersionId;
        }

        private bool isNewRecord;
        public bool IsNewRecord
        {
            get { return isNewRecord; }
        }

        protected virtual void Updated(object versionId)
        {
            this.isNewRecord = false;
            this.concurencyId = versionId;
        }

        protected virtual void Deleted()
        {
        }

        private Guid transactionKey;
        public Guid TransactionKey
        {
            get { return transactionKey; }
        }

        public void UseTransaction(Guid key)
        {
            transactionKey = key;
        }

        public void BeginTransaction()
        {
            BeginTransaction(CreateDal());
        }

        protected void BeginTransaction(Acms.SqlDal.Base dal)
        {
            if (transactionKey != Guid.Empty)
            {
                throw new ApplicationException("This class is already using a transaction");
            }
            else
            {
                transactionKey = dal.StartTransaction();
            }
        }

        public void CommitTransaction()
        {
            CommitTransaction(CreateDal());
        }

        protected void CommitTransaction(Acms.SqlDal.Base dal)
        {
            if (transactionKey == Guid.Empty)
            {
                throw new ApplicationException("This class is not using a transaction");
            }
            else
            {
                dal.CommitTransaction(transactionKey);
                this.transactionKey = Guid.Empty;
            }
        }

        public void RollbackTransaction()
        {
            RollbackTransaction(CreateDal());
        }

        protected void RollbackTransaction(Acms.SqlDal.Base dal)
        {
            if (transactionKey != Guid.Empty)
            {
                dal.RollbackTransaction(transactionKey);
                this.transactionKey = Guid.Empty;
            }
        }

        protected abstract Acms.SqlDal.Base CreateDal();

        protected virtual void SaveChanges(ref object concurencyId, SqlDal.Base dal)
        {
            throw new NotImplementedException("Method Save Changes must be overriden if Update is called");
        }

        protected void Update()
        {
            SqlDal.Base dal = CreateDal();
            if (TransactionKey != Guid.Empty)
            {
                dal.UseTransaction(TransactionKey);
            }
            try
            {
                SaveChanges(ref concurencyId, dal);
            }
            catch
            {
                if (this.TransactionKey != Guid.Empty)
                {
                    RollbackTransaction();
                }
                throw;
            }
            Updated(ConcurencyId);
        }

        protected virtual void DeleteRecord(object concurencyId, SqlDal.Base dal)
        {
            throw new NotImplementedException("Method DeleteRecord must be overriden if Delete is called");
        }

        protected void Delete()
        {
            SqlDal.Base dal = CreateDal();
            if (TransactionKey != Guid.Empty)
            {
                dal.UseTransaction(TransactionKey);
            }
            DeleteRecord(ConcurencyId, dal);
            Deleted();
        }

        public bool CheckConcurent(object concurencyId)
        {
            bool isConcurrent = false;
            if (this.ConcurencyId == null && concurencyId == null)
            {
                isConcurrent = true;
            }
            else if ((this.ConcurencyId == null && concurencyId != null) || (this.ConcurencyId != null && concurencyId == null))
            {
                isConcurrent = false;
            }
            else
            {
                isConcurrent = this.ConcurencyId.Equals(concurencyId);
            }
            return isConcurrent;
        }

        protected object LoadConcurencyId(Hashtable table)
        {
            if (table.ContainsKey(SqlDal.Base.BaseFields.VersionId))
            {
                return table[SqlDal.Base.BaseFields.VersionId];
            }
            else
            {
                return null;
            }
        }

        internal static void SortList<T>(List<T> list, string expression, BaseComparison<T> comparison)
        {
            if (expression != null)
            {
                expression = expression.Trim();
            }
            if (!string.IsNullOrEmpty(expression))
            {
                bool globalAssending = true;
                //if form is (field1 asc, field2 desc) desc need to revers internal orders
                if (expression.StartsWith("(") && expression.ToLower().EndsWith(") desc"))
                {
                    globalAssending = false;
                    expression = expression.Substring(1, expression.Length - 7);
                }
                else if (expression.StartsWith("(") && expression.ToLower().EndsWith(")"))
                {
                    expression = expression.Substring(1, expression.Length - 2);
                }

                string[] tmpArr = expression.Split(',');
                for (int i = tmpArr.Length - 1; i >= 0; i--)
                {
                    string t = tmpArr[i].Trim();
                    string field = null;
                    bool ascending = true;
                    if (t.ToLower().EndsWith(" desc"))
                    {
                        field = t.Substring(0, t.Length - 5);
                        ascending = false;
                    }
                    else if (t.ToLower().EndsWith(" asc"))
                    {
                        field = t.Substring(0, t.Length - 4);
                    }
                    else
                    {
                        field = t;
                    }
                    //switch if gloabal asscending is false
                    if (!globalAssending)
                    {
                        ascending = !ascending;
                    }
                    comparison.Set(field, ascending);
                    list.Sort(comparison);
                }
            }
        }
    }
}
